{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn.functional as F\n",
    "import math\n",
    "from torch.autograd import Variable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "class FunLSQ(torch.autograd.Function):\n",
    "    @staticmethod\n",
    "    def forward(ctx, weight, alpha, g, Qn, Qp):\n",
    "        assert alpha > 0, 'alpha = {}'.format(alpha)\n",
    "        ctx.save_for_backward(weight, alpha)\n",
    "        ctx.other = g, Qn, Qp\n",
    "        q_w = (weight / alpha).round().clamp(Qn, Qp)\n",
    "        w_q = q_w * alpha\n",
    "        return w_q\n",
    "\n",
    "    @staticmethod\n",
    "    def backward(ctx, grad_weight):\n",
    "        weight, alpha = ctx.saved_tensors\n",
    "        g, Qn, Qp = ctx.other\n",
    "        q_w = weight / alpha\n",
    "        indicate_small = (q_w < Qn).float()\n",
    "        indicate_big = (q_w > Qp).float()\n",
    "        indicate_middle = torch.ones(indicate_small.shape).to(indicate_small.device) - indicate_small - indicate_big\n",
    "        grad_alpha = ((indicate_small * Qn + indicate_big * Qp + indicate_middle * (\n",
    "                -q_w + q_w.round())) * grad_weight * g).sum().unsqueeze(dim=0)\n",
    "        grad_weight = indicate_middle * grad_weight\n",
    "        # The following operation can make sure that alpha is always greater than zero in any case and can also\n",
    "        # suppress the update speed of alpha. (Personal understanding)\n",
    "#         grad_alpha.clamp_(-alpha.item(), alpha.item())  # FYI\n",
    "        return grad_weight, grad_alpha, None, None, None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def grad_scale(x, scale):\n",
    "    y = x\n",
    "    y_grad = x * scale\n",
    "    return y.detach() - y_grad.detach() + y_grad\n",
    "\n",
    "def round_pass(x):\n",
    "    y = x.round()\n",
    "    y_grad = x\n",
    "    return y.detach() - y_grad.detach() + y_grad"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialize the variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "nbits = 4\n",
    "Qn = -2 ** (nbits - 1)\n",
    "Qp = 2 ** (nbits - 1) - 1\n",
    "# g = math.sqrt(weight.numel() * Qp)\n",
    "g = 1.0/2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Method1 accroding to the LSQ paper:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "ws = []\n",
    "wqs = []\n",
    "alpha_grads = []\n",
    "for i in range(1000):\n",
    "    weight = Variable(torch.Tensor([i * 0.01]), requires_grad=True)\n",
    "    alpha = torch.ones(1, requires_grad=True)\n",
    "    ws.append(weight.data[0])\n",
    "    alpha2 = grad_scale(alpha, g)\n",
    "    w = weight / alpha2\n",
    "    w = w.clamp(Qn, Qp)\n",
    "    q_w = round_pass(w)\n",
    "    w_q = q_w * alpha2\n",
    "    wqs.append(w_q.data[0])\n",
    "    w_q.backward()\n",
    "    alpha_grads.append(alpha.grad)\n",
    "# print(ws)\n",
    "# print(wqs)\n",
    "# print(alpha_grads)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# Data for plotting\n",
    "\n",
    "plt.subplot(2, 1, 1)\n",
    "\n",
    "plt.plot(ws, wqs)\n",
    "plt.title('LSQuantization')\n",
    "plt.ylabel('vhat')\n",
    "\n",
    "plt.subplot(2, 1, 2)\n",
    "\n",
    "plt.plot(ws, alpha_grads)\n",
    "plt.ylabel('\\partial vhat / alpha')\n",
    "plt.xlabel('v')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Method2:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "ws = []\n",
    "wqs = []\n",
    "alpha_grads = []\n",
    "for i in range(1000):\n",
    "    weight = Variable(torch.Tensor([i * 0.01]), requires_grad=True)\n",
    "    alpha = torch.ones(1, requires_grad=True)\n",
    "    ws.append(weight.data[0])\n",
    "    w_q = FunLSQ.apply(weight, alpha, g, Qn, Qp)\n",
    "    wqs.append(w_q.data[0])\n",
    "    w_q.backward(retain_graph=True)\n",
    "    alpha_grads.append(alpha.grad)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(2, 1, 1)\n",
    "\n",
    "plt.plot(ws, wqs)\n",
    "plt.title('LSQuantization')\n",
    "plt.ylabel('vhat')\n",
    "\n",
    "plt.subplot(2, 1, 2)\n",
    "\n",
    "plt.plot(ws, alpha_grads)\n",
    "plt.ylabel('\\partial vhat / alpha')\n",
    "plt.xlabel('v')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
